import os
import csv
import xlrd
import random
import numpy as np
import datetime
import json
from scipy.spatial import distance

# Change directory to the folder containing this file
os.chdir("D://Dropbox//Solidarity//replication")

# Load giving data
giving_data = {}
with open(".//Data//full_member_to_candidate_giving.csv", 'r') as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        congress, year, icpsrid, recipid, lpac_to_cmte, cmte_to_cmte, lpac_to_lpac = row
        total_giving = int(lpac_to_cmte) + int(cmte_to_cmte) + int(lpac_to_lpac)
        if year not in giving_data:
            giving_data[year] = {}
        if icpsrid not in giving_data[year]:
            giving_data[year][icpsrid] = {}
        if recipid not in giving_data[year][icpsrid]:
            giving_data[year][icpsrid][recipid] = 0
        giving_data[year][icpsrid][recipid] += total_giving

# Load map from FEC ids to ICPSR IDs
fec_map = {}
with open(".//Data//fec_icpsr_ids.csv", 'r') as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        year, icpsr, cand_id = row
        if len(icpsr) > 5:
            continue
        if icpsr not in fec_map:
            fec_map[cand_id] = icpsr
        else:
            if fec_map[cand_id] != icpsr:
                print(icpsr + " " + cand_id + " " + fec_map[cand_id])

election_data = {}
race_map = {}
gender_map = {}

# Ingest Fraga data to get race and gender of each candidate
with open(".//Data//FragaData.csv", 'r') as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        year, state, district, fecid, party, firstname, lastname, gender, race = row
        if year not in election_data:
            election_data[year] = {}
        seat = state + ":" + district
        if seat not in election_data[year]:
            election_data[year][seat] = {}
        party = party[0]
        if party not in election_data[year][seat]:
            election_data[year][seat][party] = []
        name = firstname + " " + lastname
        if district == "0" or district == "0.1":
            chamber = "Senate"
        else:
            chamber = "House"
        election_data[year][seat][party].append([name, fecid, gender, race, chamber])

        if fecid in fec_map:
            race_map[fec_map[fecid]] = race
            gender_map[fec_map[fecid]] = gender

# Supplement Fraga data with Senate candidates from 2006 to 2010
with open(".//Data//GeneralSenateCandidates_06-10.csv", 'r') as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        year, state, fecid, party, firstname, lastname, gender, race, _ = row
        if year not in election_data:
            election_data[year] = {}
        seat = state + ":0"
        if seat not in election_data[year]:
            election_data[year][seat] = {}
        party = party[0]
        if party not in election_data[year][seat]:
            election_data[year][seat][party] = []
        name = firstname + " " + lastname
        chamber = "Senate"
        election_data[year][seat][party].append([name, fecid, gender, race, chamber])

        if fecid in fec_map:
            race_map[fec_map[fecid]] = race
            gender_map[fec_map[fecid]] = gender

# Extend Fraga data out to 2020
with open(".//Data//general_candidates_2020_complete.csv", "r") as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        year, state, district, fecid, party, firstname, lastname, gender, race = row
        if year not in election_data:
            election_data[year] = {}
        seat = state + ":" + district
        if seat not in election_data[year]:
            election_data[year][seat] = {}
        party = party[0]
        if party not in election_data[year][seat]:
            election_data[year][seat][party] = []
        name = firstname + " " + lastname
        if district == "0" or district == "0.1":
            chamber = "Senate"
        else:
            chamber = "House"
        election_data[year][seat][party].append([name, fecid, gender, race, chamber])

        if fecid in fec_map:
            race_map[fec_map[fecid]] = race
            gender_map[fec_map[fecid]] = gender

# There appear to be some errors in the Fraga data.  Patcho those wiht these files
with open(".//Data//HousePatch.csv", "r", encoding = "utf-8") as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        leg, name, state, race, gender = row
        gender_map[leg] = gender
        race_map[leg] = race

with open(".//Data//SenatePatch.csv", "r", encoding = "utf-8") as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        leg, race, gender = row
        gender_map[leg] = gender
        race_map[leg] = race

state_abbrevs = [ 'AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DE', 'FL', 'GA',
           'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME',
           'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM',
           'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX',
           'UT', 'VA', 'VT', 'WA', 'WI', 'WV', 'WY']

# Get DW-NOMINATE scores and merge with race/gender data
leg_data = {}
with open(".//Data//HSall_members.csv", 'r', encoding = "utf-8") as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        congress, chamber, leg, _, district, state, partyid = row[0:7]
        year = (int(congress) - 104)*2 + 1996
        if year < 2006 or year > 2020 or state not in state_abbrevs:
            continue
        year = str(year)
        if chamber == "President":
            continue
        if chamber not in leg_data:
            leg_data[chamber] = {}
        if year not in leg_data[chamber]:
            leg_data[chamber][year] = {}
        seat = state + ":" + district

        if len(row[13]) > 0:
            nominate = float(row[13])
        else:
            nominate = "NA"

        if partyid == "100":
            party = "D"
        elif partyid == "200":
            party = "R"
        else:
            continue

        try:
            gender = gender_map[leg]
            race = race_map[leg]
            leg_data[chamber][year][leg] = [seat, nominate, party, gender, race]
        except:
            leg_data[chamber][year][leg] = [seat, nominate, party]

# Get list of mentors
mentors = set()
with open(".//Data//mentors.csv", 'r', encoding = "utf-8") as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        mentors.add(row[0])

# Get count of civiil rights bills introduced by each legislator
civil = {}
with open(".//Data//civil_issue_bills.csv", 'r', encoding = "utf-8") as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        icpsr, congress, _, civil_bills = row
        if icpsr not in civil:
            civil[icpsr] = {}
        civil[icpsr][int(congress)] = int(civil_bills)


leaders = {"H" : {}, "S" : {}}
with open(".//Data//categorized_leadership.csv", 'r', encoding = "utf-8") as f:
    responsereader = csv.reader(f, delimiter = ',', quotechar = '"')
    next(responsereader)
    for row in responsereader:
        _, icpsr, congress, chamber, terminal_pos, mezz_pos = row
        if icpsr not in leaders[chamber]:
            leaders[chamber][icpsr] = {}
        leaders[chamber][icpsr][int(congress)] = [int(terminal_pos), int(mezz_pos)]

# Compile list of all races with female candidates (split by party)
# Compile a similar list of all races with poc candidates
# Compile a similar list of all races with intersectional candidates
woman_races = {}
poc_races = {}
intersectional_races = {}
for year in election_data:
    for seat in election_data[year]:
        if "D" not in election_data[year][seat] or "R" not in election_data[year][seat]:
            continue
        if max(len(election_data[year][seat]["D"]), len(election_data[year][seat]["R"])) > 1:
            continue
        dem_gender = election_data[year][seat]["D"][0][2]
        rep_gender = election_data[year][seat]["R"][0][2]
        dem_race = election_data[year][seat]["D"][0][3]
        rep_race = election_data[year][seat]["R"][0][3]
        chamber = election_data[year][seat]["R"][0][4]

        if dem_gender == "F":
            if chamber not in woman_races:
                woman_races[chamber] = {}
            if year not in woman_races[chamber]:
                woman_races[chamber][year] = {}
            if "D" not in woman_races[chamber][year]:
                woman_races[chamber][year]["D"] = []
            woman_races[chamber][year]["D"].append([seat, dem_race, dem_gender, rep_race, rep_gender])
        if rep_gender == "F":
            if chamber not in woman_races:
                woman_races[chamber] = {}
            if year not in woman_races[chamber]:
                woman_races[chamber][year] = {}
            if "R" not in woman_races[chamber][year]:
                woman_races[chamber][year]["R"] = []
            woman_races[chamber][year]["R"].append([seat, rep_race, rep_gender, dem_race, dem_gender])

        if dem_race != "White":
            if chamber not in poc_races:
                poc_races[chamber] = {}
            if year not in poc_races[chamber]:
                poc_races[chamber][year] = {}
            if "D" not in poc_races[chamber][year]:
                poc_races[chamber][year]["D"] = []
            poc_races[chamber][year]["D"].append([seat, dem_race, dem_gender, rep_race, rep_gender])
        if rep_race != "White":
            if chamber not in poc_races:
                poc_races[chamber] = {}
            if year not in poc_races[chamber]:
                poc_races[chamber][year] = {}
            if "R" not in poc_races[chamber][year]:
                poc_races[chamber][year]["R"] = []
            poc_races[chamber][year]["R"].append([seat, rep_race, rep_gender, dem_race, dem_gender])

        if dem_race != "White" and dem_gender == "F":
            if chamber not in intersectional_races:
                intersectional_races[chamber] = {}
            if year not in intersectional_races[chamber]:
                intersectional_races[chamber][year] = {}
            if "D" not in intersectional_races[chamber][year]:
                intersectional_races[chamber][year]["D"] = []
            intersectional_races[chamber][year]["D"].append([seat, dem_race, dem_gender, rep_race, rep_gender])
        if rep_race != "White" and rep_gender == "F":
            if chamber not in intersectional_races:
                intersectional_races[chamber] = {}
            if year not in intersectional_races[chamber]:
                intersectional_races[chamber][year] = {}
            if "R" not in intersectional_races[chamber][year]:
                intersectional_races[chamber][year]["R"] = []
            intersectional_races[chamber][year]["R"].append([seat, rep_race, rep_gender, dem_race, dem_gender])

# Helper function for determining if two legislators represent the same state
def isSameState(seat1, seat2):
    state1 = seat1.split(":")[0]
    state2 = seat2.split(":")[0]
    if state1 == state2:
        return 1
    else:
        return 0

# Helper function for determining if two legislators of the same gender are also of the same race
# and if two legislators of the same race are also of the same gender
def isSameOtherIdentity(desid, race, gender, cand_race, cand_gender):
    if desid == "Woman":
        if cand_race == race:
            return 1
        else:
            return 0
    else:
        if cand_gender == gender:
            return 1
        else:
            return 0

# Helper function for matching a donor to a similar legislator
# Can be toggled based on whether the outcome is whether the donor donated or the
# amount the donor donated
def findMatch(donor, receiver, chamber, year, seat, desid, cand_race, cand_gender, amount = False):
    # Pull donor's characteristics
    donor_seat, donor_nominate, donor_party, donor_gender, donor_race = leg_data[chamber][year][donor]
    donor_same_state = isSameState(donor_seat, seat)
    donor_same_other_identity = isSameOtherIdentity(desid, donor_race, donor_gender, cand_race, cand_gender)

    # Pull donor's donation activity (the relevant activity depends on whether we are using the amount
    # or a binary indicator of giving as the outcome)
    if amount:
        if donor in giving_data[year]:
            donor_total_giving = sum(giving_data[year][donor].values())
            if receiver in giving_data[year][donor]:
                donor_total_giving -= giving_data[year][donor][receiver]
        else:
            donor_total_giving = 0
        donor_activity_control = donor_total_giving
    else:
        if donor in giving_data[year]:
            donor_num_receivers = len(giving_data[year][donor])
            if receiver in giving_data[year][donor]:
                donor_num_receivers -= 1
        else:
            donor_num_receivers = 0
        donor_activity_control = donor_num_receivers

    # Iterate through all other legislators to get the weighting matrix
    # for the Mahalnobis distance
    best_match = ""
    min_dist = 100000000000000
    num_receivers_list = []
    total_giving_list = []
    nominate_list = []
    same_state_list = []
    same_other_identity_list = []
    for possible_match in leg_data[chamber][year]:
        # Ensure we have the prospective match's race and gender
        if len(leg_data[chamber][year][possible_match]) < 5:
            continue
        # Pull characteristics of prospective match
        pmatch_seat, pmatch_nominate, pmatch_party, pmatch_gender, pmatch_race = leg_data[chamber][year][possible_match]
        pmatch_same_state = isSameState(pmatch_seat, seat)
        # Ensure they are of the same party and they have a NOMINATE score
        if pmatch_party != donor_party or pmatch_nominate == "NA" or \
        pmatch_seat == seat:
            continue
        # Pull remaining characteristics of prospective match
        pmatch_same_other_identity = isSameOtherIdentity(desid, pmatch_race, pmatch_gender, cand_race, cand_gender)
        if amount:
            if possible_match in giving_data[year]:
                pmatch_total_giving = sum(giving_data[year][possible_match].values())
                if receiver in giving_data[year][possible_match]:
                    pmatch_total_giving -= giving_data[year][possible_match][receiver]
            else:
                pmatch_total_giving = 0
            total_giving_list.append(pmatch_total_giving)
        else:
            if possible_match in giving_data[year]:
                pmatch_num_receivers = len(giving_data[year][possible_match])
                if receiver in giving_data[year][possible_match]:
                    pmatch_num_receivers -= 1
            else:
                pmatch_num_receivers = 0
            num_receivers_list.append(pmatch_num_receivers)

        # Record their values for the matching variable
        nominate_list.append(pmatch_nominate)
        same_state_list.append(pmatch_same_state)
        same_other_identity_list.append(pmatch_same_other_identity)

    # The nearest neighbor routine will break if there is no variation in one
    # of the matching variables, so select down to the ones that actually
    # exhibit variation
    use_vector = [0, 1, 2, 3]
    if len(set(same_state_list)) == 1:
        use_vector.remove(2)
    if len(set(same_other_identity_list)) == 1:
        use_vector.remove(3)

    # Calculate Sigma for Mahalnobis distance
    try:
        if amount:
            activity_control_list = total_giving_list
        else:
            activity_control_list = num_receivers_list

        SigmaInv = np.linalg.inv(np.cov(np.stack((activity_control_list, nominate_list, same_state_list, same_other_identity_list), axis = 0)[use_vector]))
    except:
        print("Could not calculate inverse for matrix of length " + str(len(nominate_list)))
        SigmaInv = np.identity(len(use_vector))

    # Iterate through each possible match to find which is closest by Mahalnobis distnace
    for possible_match in leg_data[chamber][year]:
        # Ensure their race and gender is known
        if len(leg_data[chamber][year][possible_match]) < 5:
            continue
        # Pull their characteristics
        pmatch_seat, pmatch_nominate, pmatch_party, pmatch_gender, pmatch_race = leg_data[chamber][year][possible_match]
        pmatch_same_state = isSameState(pmatch_seat, seat)

        # If we're looking for a control for a woman, make sure the possible match is a man
        # If we're looking for a control for a poc, make sure the possible match is white
        accept = reject_function(desid, pmatch_gender, pmatch_race)
        if desid not in ["pocman", "whitewoman"] and accept:
            continue
        elif desid in ["pocman", "whitewoman"] and not accept:
            continue
        # Make sure they're members of the same party, their NOMINATE score is known,
        # and it's not the same legislator
        if pmatch_party != donor_party or pmatch_nominate == "NA" or \
        pmatch_seat == seat:
            continue
        # Get remaining matching variables
        pmatch_same_other_identity = isSameOtherIdentity(desid, pmatch_race, pmatch_gender, cand_race, cand_gender)
        if amount:
            if possible_match in giving_data[year]:
                pmatch_total_giving = sum(giving_data[year][possible_match].values())
                if receiver in giving_data[year][possible_match]:
                    pmatch_total_giving -= giving_data[year][possible_match][receiver]
            else:
                pmatch_total_giving = 0

            pmatch_activity_control = pmatch_total_giving
        else:
            if possible_match in giving_data[year]:
                pmatch_num_receivers = len(giving_data[year][possible_match])
                if receiver in giving_data[year][possible_match]:
                    pmatch_num_receivers -= 1
            else:
                pmatch_num_receivers = 0
            pmatch_activity_control = pmatch_num_receivers

        # Calculate Mahalnobis distance
        match_dist = distance.mahalanobis([[pmatch_activity_control, pmatch_nominate, pmatch_same_state, pmatch_same_other_identity][i] for i in use_vector], \
        [[donor_activity_control, donor_nominate, donor_same_state, donor_same_other_identity][i] for i in use_vector], \
        SigmaInv)

        # Record if they're the best match seen so far
        if match_dist < min_dist:
            min_dist = match_dist
            best_match = possible_match

    return best_match

# A helper function for determining if the legislator has the correct identity
# for the relevant part of the analysis
def reject_function(desid, gender, race):
    if desid == "Woman":
        if gender == "F":
            return True
        else:
            return False
    if desid == "poc":
        if race == "White":
            return False
        else:
            return True
    if desid == "pocman":
        if race != "White" and gender == "M":
            return True
        else:
            return False
    if desid == "whitewoman":
        if race == "White" and gender == "F":
            return True
        else:
            return False

# Builds the data; flexible so that we can use the same function to pull
# the gender and race data, with both outcome variables
def build_data(copoutf, relevant_races, desid, amount = False):
    if amount:
        activity_name = "giving"
        activity_control_name = "other_giving"
    else:
        activity_name = "gave"
        activity_control_name = "num_other_recipients"

    copoutf.write("giver,receiver,seat,year,chamber,party,gender,race," + activity_name + "," + activity_control_name + \
    ",nominate,same_state,matchid,cand_race,cand_gender,opp_race,opp_gender,cop_incumbent,opp_incumbent,mentor,civil_bills,terminal_pos,mezz_pos\n")

    # Iterate through races where a member of the correct identity group is running)
    for chamber in relevant_races:
        for year in relevant_races[chamber]:
            for party in relevant_races[chamber][year]:
                for seat, cand_race, cand_gender, opp_race, opp_gender in relevant_races[chamber][year][party]:
                    # Iterate through incumbents
                    for leg in leg_data[chamber][year]:
                        # Skip incumbents whose race and gender is unkown
                        if len(leg_data[chamber][year][leg]) < 5:
                            continue
                        # Pull characteristics of incumbent
                        leg_seat, leg_nominate, leg_party, leg_gender, leg_race = leg_data[chamber][year][leg]
                        same_state = isSameState(seat, leg_seat)

                        # Ensure we're talking about a legislator other than the candidate, that we know
                        # their NOMINATE score, and that they are of the correct demographics
                        if leg_seat == seat or leg_nominate == "NA":
                            continue
                        accept = reject_function(desid, leg_gender, leg_race)
                        if not accept:
                            continue

                        if leg_party == "R":
                            oppparty = "D"
                        else:
                            oppparty = "R"

                        if leg in mentors:
                            mentor = 1
                        else:
                            mentor = 0

                        try:
                            civil_bills = civil[leg][(int(year)-2010)/2 + 111]
                        except:
                            civil_bills = 0

                        try:
                            terminal_pos, mezz_pos = leaders[chamber[0]][leg][(int(year)-2010)/2 + 111]
                        except:
                            terminal_pos, mezz_pos = [0,0]

                        # Figure out if the candidate is an incumbent
                        # or a challenger
                        partyscand = election_data[year][seat][leg_party][0][1]

                        try:
                            cand_icpsr = fec_map[partyscand]
                            if cand_icpsr in leg_data[chamber][year]:
                                cop_incumbent = 1
                            else:
                                cop_incumbent = 0
                        except:
                            cop_incumbent = 0

                        try:
                            oppcand = election_data[year][seat][oppparty][0][1]
                            opp_cand_icpsr = fec_map[oppcand]
                            if opp_cand_icpsr in leg_data[chamber][year]:
                                opp_incumbent = 1
                            else:
                                opp_incumbent = 0
                        except:
                            opp_incumbent = 0

                        # Get the legislator's other giving
                        if amount:
                            giving = 0
                            if leg in giving_data[year]:
                                if partyscand in giving_data[year][leg]:
                                    if giving_data[year][leg][partyscand] > 0:
                                        giving = giving_data[year][leg][partyscand]
                                    else:
                                        giving = 0
                            other_giving = 0
                            if leg in giving_data[year]:
                                other_giving = sum(giving_data[year][leg].values()) - giving
                        else:
                            gave = 0
                            if leg in giving_data[year]:
                                if partyscand in giving_data[year][leg]:
                                    if giving_data[year][leg][partyscand] > 0:
                                        gave = 1
                                    else:
                                        gave = 0
                            other_recipients = 0
                            if leg in giving_data[year]:
                                other_recipients = len(giving_data[year][leg]) - gave

                        # Find the best match
                        match = findMatch(leg, partyscand, chamber, year, seat, desid, cand_race, cand_gender, \
                        amount = amount)

                        # Recover if no match is found
                        if len(match) == 0:
                            continue

                        # Get match's characteristics
                        match_seat, match_nominate, match_party, match_gender, match_race = leg_data[chamber][year][match]
                        match_same_state = isSameState(seat, match_seat)

                        if match in mentors:
                            match_mentor = 1
                        else:
                            match_mentor = 0

                        try:
                            match_civil_bills = civil[match][(int(year)-2010)/2 + 111]
                        except:
                            match_civil_bills = 0

                        if amount:
                            match_giving = 0
                            if match in giving_data[year]:
                                if partyscand in giving_data[year][match]:
                                    if giving_data[year][match][partyscand] > 0:
                                        match_giving = giving_data[year][match][partyscand]
                                    else:
                                        match_giving = 0
                            match_other_giving = 0
                            if match in giving_data[year]:
                                match_other_giving = sum(giving_data[year][match].values()) - match_giving
                        else:
                            match_gave = 0
                            if match in giving_data[year]:
                                if partyscand in giving_data[year][match]:
                                    if giving_data[year][match][partyscand] > 0:
                                        match_gave = 1
                                    else:
                                        match_gave = 0
                            match_other_recipients = 0
                            if match in giving_data[year]:
                                match_other_recipients = len(giving_data[year][match]) - match_gave

                        try:
                            match_terminal_pos, match_mezz_pos = leaders[chamber[0]][match][(int(year)-2010)/2 + 111]
                        except:
                            match_terminal_pos, match_mezz_pos = [0,0]

                        # Record data
                        if amount:
                            out = [leg, partyscand, seat, year, chamber, leg_party, leg_gender, \
                            leg_race, giving, other_giving, leg_nominate, same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, mentor, civil_bills, terminal_pos, mezz_pos]
                            match_out = [match, partyscand, seat, year, chamber, match_party, match_gender, \
                            match_race, match_giving, match_other_giving, match_nominate, match_same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, match_mentor, match_civil_bills, match_terminal_pos, match_mezz_pos]
                        else:
                            out = [leg, partyscand, seat, year, chamber, leg_party, leg_gender, \
                            leg_race, gave, other_recipients, leg_nominate, same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, mentor, civil_bills, terminal_pos, mezz_pos]
                            match_out = [match, partyscand, seat, year, chamber, match_party, match_gender, \
                            match_race, match_gave, match_other_recipients, match_nominate, match_same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, match_mentor, match_civil_bills, match_terminal_pos, match_mezz_pos]

                        if leg_party == party:
                            copoutf.write(",".join([str(x) for x in out]) + '\n')
                            copoutf.write(",".join([str(x) for x in match_out]) + '\n')

# Builds the intersectional data for Table 13
def build_intersectional_data(copoutf, relevant_races, amount = False):
    if amount:
        activity_name = "giving"
        activity_control_name = "other_giving"
    else:
        activity_name = "gave"
        activity_control_name = "num_other_recipients"

    copoutf.write("giver,receiver,seat,year,chamber,party,gender,race," + activity_name + "," + activity_control_name + \
    ",nominate,same_state,matchid,cand_race,cand_gender,opp_race,opp_gender,cop_incumbent,opp_incumbent,civil_bills\n")

    # Iterate through races where a minority woman is running
    for chamber in relevant_races:
        for year in relevant_races[chamber]:
            for party in relevant_races[chamber][year]:
                for seat, cand_race, cand_gender, opp_race, opp_gender in relevant_races[chamber][year][party]:
                    # Iterate through incumbents
                    for leg in leg_data[chamber][year]:
                        # Skip incumbents whose race and gender is unkown
                        if len(leg_data[chamber][year][leg]) < 5:
                            continue
                        leg_seat, leg_nominate, leg_party, leg_gender, leg_race = leg_data[chamber][year][leg]
                        same_state = isSameState(seat, leg_seat)

                        # Make sure the legislator is not the same person as the canidate,
                        # is a woman, is non-white, and has a NOMINATE score
                        if leg_seat == seat or leg_nominate == "NA":
                            continue
                        if leg_gender != "F" or leg_race == "White":
                            continue

                        if leg_party == "R":
                            oppparty = "D"
                        else:
                            oppparty = "R"

                        partyscand = election_data[year][seat][leg_party][0][1]

                        # Record whether the candidate is an incumbent or a challenger
                        try:
                            cand_icpsr = fec_map[partyscand]
                            if cand_icpsr in leg_data[chamber][year]:
                                cop_incumbent = 1
                            else:
                                cop_incumbent = 0
                        except:
                            cop_incumbent = 0

                        try:
                            oppcand = election_data[year][seat][oppparty][0][1]
                            cand_icpsr = fec_map[oppcand]
                            if cand_icpsr in leg_data[chamber][year]:
                                opp_incumbent = 1
                            else:
                                opp_incumbent = 0
                        except:
                            opp_incumbent = 0

                        # Get legislator's other giving activity
                        if amount:
                            giving = 0
                            if leg in giving_data[year]:
                                if partyscand in giving_data[year][leg]:
                                    if giving_data[year][leg][partyscand] > 0:
                                        giving = giving_data[year][leg][partyscand]
                                    else:
                                        giving = 0
                            other_giving = 0
                            if leg in giving_data[year]:
                                other_giving = sum(giving_data[year][leg].values()) - giving
                        else:
                            gave = 0
                            if leg in giving_data[year]:
                                if partyscand in giving_data[year][leg]:
                                    if giving_data[year][leg][partyscand] > 0:
                                        gave = 1
                                    else:
                                        gave = 0
                            other_recipients = 0
                            if leg in giving_data[year]:
                                other_recipients = len(giving_data[year][leg]) - gave

                        # Match legislator to a white female and to a male person of color
                        white_match = findMatch(leg, partyscand, chamber, year, seat, "whitewoman", cand_race, cand_gender, amount = amount)
                        poc_match = findMatch(leg, partyscand, chamber, year, seat, "pocman", cand_race, cand_gender, amount = amount)

                        if len(white_match) == 0 or len(poc_match) == 0:
                            continue

                        # Pull characteristics for both matches
                        white_match_seat, white_match_nominate, white_match_party, white_match_gender, white_match_race = leg_data[chamber][year][white_match]
                        white_match_same_state = isSameState(seat, white_match_seat)

                        if amount:
                            white_match_giving = 0
                            if white_match in giving_data[year]:
                                if partyscand in giving_data[year][white_match]:
                                    if giving_data[year][white_match][partyscand] > 0:
                                        white_match_giving = giving_data[year][white_match][partyscand]
                                    else:
                                        white_match_giving = 0
                            white_match_other_giving = 0
                            if white_match in giving_data[year]:
                                white_match_other_giving = sum(giving_data[year][white_match].values()) - white_match_giving
                        else:
                            white_match_gave = 0
                            if white_match in giving_data[year]:
                                if partyscand in giving_data[year][white_match]:
                                    if giving_data[year][white_match][partyscand] > 0:
                                        white_match_gave = 1
                                    else:
                                        white_match_gave = 0
                            white_match_other_recipients = 0
                            if white_match in giving_data[year]:
                                white_match_other_recipients = len(giving_data[year][white_match]) - white_match_gave

                        poc_match_seat, poc_match_nominate, poc_match_party, poc_match_gender, poc_match_race = leg_data[chamber][year][poc_match]
                        poc_match_same_state = isSameState(seat, poc_match_seat)

                        if amount:
                            poc_match_giving = 0
                            if poc_match in giving_data[year]:
                                if partyscand in giving_data[year][poc_match]:
                                    if giving_data[year][poc_match][partyscand] > 0:
                                        poc_match_giving = giving_data[year][poc_match][partyscand]
                                    else:
                                        poc_match_giving = 0
                            poc_match_other_giving = 0
                            if poc_match in giving_data[year]:
                                poc_match_other_giving = sum(giving_data[year][poc_match].values()) - poc_match_giving
                        else:
                            poc_match_gave = 0
                            if poc_match in giving_data[year]:
                                if partyscand in giving_data[year][poc_match]:
                                    if giving_data[year][poc_match][partyscand] > 0:
                                        poc_match_gave = 1
                                    else:
                                        poc_match_gave = 0
                            poc_match_other_recipients = 0
                            if poc_match in giving_data[year]:
                                poc_match_other_recipients = len(giving_data[year][poc_match]) - poc_match_gave

                        try:
                            civil_bills = civil[leg][(int(year)-2010)/2 + 111]
                        except:
                            civil_bills = 0
                        try:
                            white_match_civil_bills = civil[white_match][(int(year)-2010)/2 + 111]
                        except:
                            white_match_civil_bills = 0
                        try:
                            poc_match_civil_bills = civil[poc_match][(int(year)-2010)/2 + 111]
                        except:
                            poc_match_civil_bills = 0

                        # Record data
                        if amount:
                            out = [leg, partyscand, seat, year, chamber, leg_party, leg_gender, \
                            leg_race, giving, other_giving, leg_nominate, same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, civil_bills]
                            white_match_out = [white_match, partyscand, seat, year, chamber, white_match_party, white_match_gender, \
                            white_match_race, white_match_giving, white_match_other_giving, white_match_nominate, white_match_same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, white_match_civil_bills]
                            poc_match_out = [poc_match, partyscand, seat, year, chamber, poc_match_party, poc_match_gender, \
                            poc_match_race, poc_match_giving, poc_match_other_giving, poc_match_nominate, poc_match_same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, poc_match_civil_bills]
                        else:
                            out = [leg, partyscand, seat, year, chamber, leg_party, leg_gender, \
                            leg_race, gave, other_recipients, leg_nominate, same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, civil_bills]
                            white_match_out = [white_match, partyscand, seat, year, chamber, white_match_party, white_match_gender, \
                            white_match_race, white_match_gave, white_match_other_recipients, white_match_nominate, white_match_same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, white_match_civil_bills]
                            poc_match_out = [poc_match, partyscand, seat, year, chamber, poc_match_party, poc_match_gender, \
                            poc_match_race, poc_match_gave, poc_match_other_recipients, poc_match_nominate, poc_match_same_state, leg, cand_race, cand_gender,\
                            opp_race, opp_gender, cop_incumbent, opp_incumbent, poc_match_civil_bills]

                        if leg_party == party:
                            copoutf.write(",".join([str(x) for x in out]) + '\n')
                            copoutf.write(",".join([str(x) for x in white_match_out]) + '\n')
                            copoutf.write(",".join([str(x) for x in poc_match_out]) + '\n')

# Build data for main analysis, where treated legislators are women and control
# legislators are men
with open(".//Data//women_copartisan_donations.csv", "w") as copoutf:
    build_data(copoutf, woman_races, "Woman")
with open(".//Data//women_copartisan_donations_amount.csv", "w") as copoutf:
    build_data(copoutf, woman_races, "Woman", amount = True)

# Build data for Appendix C, Tables 11-12
with open(".//Data//poc_copartisan_donations.csv", "w") as copoutf:
    build_data(copoutf, poc_races, "poc")
with open("./Data//poc_copartisan_donations_amount.csv", "w") as copoutf:
    build_data(copoutf, poc_races, "poc", amount = True)

# Build data for Appendix C, Table 13
with open(".//Data//intersectional_copartisan_donations.csv", "w") as copoutf:
    build_intersectional_data(copoutf, intersectional_races)
with open(".//Data//intersectional_copartisan_donations_amount.csv", "w") as copoutf:
        build_intersectional_data(copoutf, intersectional_races, amount = True)
